package com.allwees.bs.module.lms.track;


import com.alibaba.fastjson.JSON;
import com.allwees.bs.module.lms.vo.MileDetails;
import com.allwees.bs.module.lms.vo.MileSubscribeDetails;
import com.allwees.bs.module.lms.vo.MileTrackDetails;
import com.allwees.bs.module.lms.vo.TraceAfterShipResponseBody;
import com.allwees.bs.module.lms.vo.TrackAfterShipDetails;
import com.allwees.bs.module.lms.vo.TrackBody;
import com.allwees.bs.module.lms.vo.TrackDetails;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

import javax.annotation.PostConstruct;
import java.util.concurrent.CompletableFuture;

/**
 * @Author Daniel
 * @Version 1.0.0
 * @Description
 * @CreateTime 2020/9/19 16:47
 */
@Component
@Slf4j
public class TrackingHelper {

    private static Tracking51Api tracking51API;
    private static TrackingAfterShipApi afterShipAPI;
    private static TrackingImileApi  mileAPI;

    @Value("${allwees.imile.basePath}")
    private String imileBasePth;

    @PostConstruct
    public void init(){
        RetrofitManager instance = RetrofitManager.getInstance(imileBasePth);
        tracking51API = instance.getTracking51Retrofit().create(Tracking51Api.class);
        afterShipAPI = instance.getAfterShipRetrofit().create(TrackingAfterShipApi.class);
        mileAPI = instance.getIMileRetrofit().create(TrackingImileApi.class);
    }

    private static final Gson GSON = new GsonBuilder().setLenient().create();

    public static void subscribe51Tracking(String trackingNum, String carrierCode){
        try {
            subscribe51Track(new Tracking51Obj(trackingNum,carrierCode));
        } catch (Exception e) {
            log.error("subscribe51Tracking error,trackingNum:{},carrierCode:{}",trackingNum,carrierCode,e);
        }
    }

    public static void subscribeAfterShipTracking(String trackingNum, String carrierCode){
        try {
            String tempCarrierCode = RetrofitManager.getAfterShipSlug(carrierCode);
            if (StringUtils.isNotEmpty(tempCarrierCode)) {
                carrierCode = tempCarrierCode;
            }
            AfterShipObj tracking = new AfterShipObj(carrierCode,trackingNum);
            subscribeAfterShipTrack(new TrackingAfterShipObj(tracking));
        } catch (Exception e) {
            log.error("subscribeAfterShipTracking error,trackingNum:{},carrierCode:{}",trackingNum,carrierCode,e);
        }
    }

    /**
     * 向51tracing订阅一条物流信息
     * @param trackingObj
     */
    private static boolean subscribe51Track(Tracking51Obj trackingObj) throws Exception {
        CompletableFuture<TrackDetails> future = new CompletableFuture<>();
        tracking51API.trackOne(trackingObj).enqueue(new ResponseCallback(future));
        TrackDetails details = future.handle((d, e) -> {
            if (e == null) {
                TrackResultCode resultCode = TrackResultCode.getResultCode(d.getMeta().getCode());
                log.info(">>> 51 track single order result code:{},message:{},trackingObj:[{}]", resultCode.getCode(), resultCode.getDescCn(), trackingObj);
                if (d.getMeta().isSuccess()) {
                    TrackBody data = d.getData();
                    log.info("===> 51 track single order success,trackNum:{},carrierCode:{},status:{}", data.getTracking_number(), data.getCarrier_code(), data.getStatus());
                }
                return d;
            } else {
                log.error(">>>51 track one error", e);
                return null;
            }
        }).get();
        return details != null && details.getMeta().isSuccess();
    }

    /**
     * 向afterShip订阅一条物流信息
     * @param trackingObj
     */
    private static boolean subscribeAfterShipTrack(TrackingAfterShipObj trackingObj) throws Exception {
        CompletableFuture<TrackAfterShipDetails> future = new CompletableFuture<>();
        afterShipAPI.trackOne(trackingObj).enqueue(new ResponseAfterShipCallback(future));
        TrackAfterShipDetails details = future.handle((d, e) -> {
            if (e == null) {
                TrackResultCode resultCode = TrackResultCode.getResultCode(d.getMeta().getCode());
                log.info(">>>after ship track single order result code:{},message:{},trackingObj:[{}]", resultCode.getCode(), resultCode.getDescCn(), trackingObj);
                if (d.getMeta().isSuccess()) {
                    TraceAfterShipResponseBody data = d.getData();
                    log.info("===>after ship track single order success,trackNum:{},carrierCode:{},status:{}",  data.getTracking().getTracking_number(),data.getTracking().getSlug(), data.getTracking().getTag());
                }
                return d;
            } else {
                log.error(">>>after ship track one error", e);
                return null;
            }
        }).get();
        return details != null && details.getMeta().isSuccess();
    }

    /**
     * 查询一条物流信息
     * @param carrierCode
     * @param trackingNum
     * @return
     * @throws Exception
     */
    public static TrackDetails get51TrackInfo(String carrierCode,String trackingNum) throws Exception {
        CompletableFuture<TrackDetails> future = new CompletableFuture<>();
        tracking51API.getInfo(carrierCode, trackingNum).enqueue(new ResponseCallback(future));
        return future.handle((d,e) ->{
            if(e == null){
                TrackResultCode resultCode = TrackResultCode.getResultCode(d.getMeta().getCode());
                log.info(">>>51 track query track info result code:{},message:{},carrierCode:[{}],trackingNum:[{}]",
                        resultCode.getCode(),resultCode.getDescCn(),carrierCode,trackingNum);
                if (!d.getMeta().isSuccess()) {
                    if (d.getMeta().getCode() == TrackResultCode.CODE_4031.getCode()){
                        subscribe51Tracking(trackingNum,carrierCode);
                    }
                    return null;
                }

                if(d.getData() == null){
                    return null;
                }
                return d;
            } else {
                log.error(">>>51 track query track info error,message:{}",e.getMessage(),e);
                return null;
            }
        }).get();
    }


    private static class ResponseCallback implements Callback<JsonObject> {

        private final CompletableFuture<TrackDetails> future;

        public ResponseCallback(CompletableFuture<TrackDetails> future) {
            this.future = future;
        }

        @Override
        public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
            JsonObject body = response.body();
            if(body == null) {
                future.complete(null);
                return;
            }
            Object data = body.get("data");
            if(data instanceof JsonArray) {
                body.add("data",null);
            }
            future.complete(GSON.fromJson(body, TrackDetails.class));
        }

        @Override
        public void onFailure(Call<JsonObject> call, Throwable t) {
            future.completeExceptionally(t);
        }
    }

    private static class ResponseAfterShipCallback implements Callback<JsonObject> {

        private final CompletableFuture<TrackAfterShipDetails> future;

        public ResponseAfterShipCallback(CompletableFuture<TrackAfterShipDetails> future) {
            this.future = future;
        }

        @Override
        public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
            JsonObject body = response.body();
            if(body == null) {
                future.complete(null);
                return;
            }
            Object data = body.get("data");
            if(data instanceof JsonArray) {
                body.add("data",null);
            }
            future.complete(GSON.fromJson(body, TrackAfterShipDetails.class));
        }

        @Override
        public void onFailure(Call<JsonObject> call, Throwable t) {
            future.completeExceptionally(t);
        }
    }

    public static TrackAfterShipDetails getTrackingByAfterShip(String carrierCode, String trackingNumber) throws Exception {
        CompletableFuture<TrackAfterShipDetails> future = new CompletableFuture<>();
        String tempCarrierCode = RetrofitManager.getAfterShipSlug(carrierCode);
        if (StringUtils.isNotEmpty(tempCarrierCode)) {
            carrierCode = tempCarrierCode;
        }
        afterShipAPI.getInfo(carrierCode, trackingNumber).enqueue(new ResponseAfterShipCallback(future));
        String finalCarrierCode = carrierCode;
        return future.handle((d, e) ->{
            if(e == null){
                if (d == null ) {
                    subscribeAfterShipTracking(trackingNumber, finalCarrierCode);
                    return  null;
                }
                TrackResultCode resultCode = TrackResultCode.getResultCode(d.getMeta().getCode());
                log.info(">>>  after ship track query track info result code:{},message:{},carrierCode:[{}],trackingNum:[{}]",
                        resultCode.getCode(),resultCode.getDescCn(), finalCarrierCode,trackingNumber);
                if (!d.getMeta().isSuccess()) {
                    if (String.valueOf(d.getMeta().getCode()).startsWith("40") ){
                        subscribeAfterShipTracking(trackingNumber, finalCarrierCode);
                    }
                    return null;
                }

                if(d.getData() == null){
                    return null;
                }
                return d;
            } else {
                log.error(">>> after ship track query track info error,message:{}",e.getMessage(),e);
                return null;
            }
        }).get();
    }

    /**
     * 向imbile订阅一条物流信息
     * @param mileRequest
     */
    public static MileSubscribeDetails subscribeMile(MileRequest mileRequest) {
        CompletableFuture<MileSubscribeDetails> future = new CompletableFuture<>();
        mileAPI.tackInfo(mileRequest).enqueue(new ResponseMileCallback<>(future,MileSubscribeDetails.class));
        future = future.handle((d, e) -> {
            log.info(">>> imile track single order,trackingObj:{}", JSON.toJSONString(mileRequest));
            if (e == null) {
                return d;
            }
            return null;
        });
        MileSubscribeDetails details;
        try {
            details = future.get();
        } catch (Exception e) {
            log.error("subscribe imile info error:{}",e.getMessage(),e);
            return null;
        }
        return details;
    }

    /**
     * 查询imile物流信息
     * @param mileTrackObj
     * @return
     * @throws Exception
     */
    public static MileTrackDetails getTrackingByMile(MileRequest mileTrackObj) throws Exception {
        CompletableFuture<MileTrackDetails> future = new CompletableFuture<>();
        mileAPI.tackInfo(mileTrackObj).enqueue(new ResponseMileCallback<>(future,MileTrackDetails.class));
        return future.handle((d, e) ->{
            if(e == null){
                if (d == null ) {
                    return  null;
                }
                TrackResultCode resultCode = TrackResultCode.getResultCode(Integer.parseInt(d.getStatus()));
                log.info(">>>  mile track query info result code:{},message:{},mileTrackObj:[{}]", resultCode.getCode(),resultCode.getDescCn(),mileTrackObj);
                if (!d.getStatus().equals("200")) {
                    return null;
                }

                if(d.getStatus() == null){
                    return null;
                }
                return d;
            } else {
                log.error(">>>  mile track query info error,message:{}",e.getMessage(),e);
                return null;
            }
        }).get();
    }

    private static class ResponseMileCallback<T extends MileDetails> implements Callback<JsonObject> {

        private final CompletableFuture<T> future;

        private Class<T> clazz;

        public ResponseMileCallback(CompletableFuture<T> future,Class<T> clazz) {
            this.future = future;
            this.clazz = clazz;
        }

        @Override
        public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
            JsonObject body = response.body();

            if(body == null) {
                future.complete(null);
                return;
            }
            future.complete(GSON.fromJson(body, clazz));
        }

        @Override
        public void onFailure(Call<JsonObject> call, Throwable t) {
            future.completeExceptionally(t);
        }
    }
}




